﻿using Autofac;
using AutoMapper;
using MDT.LifeSewagePlatform.Conmmon;
using MDT.LifeSewagePlatform.Entity;
using MDT.LifeSewagePlatform.IRepository;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MDT.LifeSewagePlatform.Extension
{
    /**
    * 命名空间: MDT.LifeSewagePlatform.Extension
    *
    * 功 能： N/A
    * 类 名： SafeListMiddleware
    *
    * Ver 变更日期 负责人 变更内容
    * ───────────────────────────────────
    * V0.01 2022/2/14 13:51:03 
    *
    *┌──────────────────────────────────┐
    *│　***************************************************．　│
    *│　**********************　　　　　　　　　　　　　　│
    *└──────────────────────────────────┘
*/
    public class SafeListMiddleware
    {
        #region prop
        private readonly RequestDelegate _next;
        private ILogger<SafeListMiddleware> _logger { get; set; }
        private IMapper mapper { get; set; }
        private IIpAddressRepository ipAddressRepository { get; set; }

        private SafeEntity safeConfiguration { get; set; }
        private Queue<IpAddressDto> qIpaddress { get; set; }

        private static List<IpAddressDto> badIps { get; set; }

        private IHttpHelper _httpHelper;
        private IJTokenMappingEntityService _jTokenMap;
        private IpInfoService _pInfoService;

        private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();

        /// <summary>
        /// 初始化一个<see cref="HostHttpCryptoMiddleware"/>类型的新实例
        /// </summary>
        /// 
        #endregion


        #region ctor

        public SafeListMiddleware(RequestDelegate next, ILogger<SafeListMiddleware> logger,
            Queue<IpAddressDto> _qpAddress, IIpAddressRepository IpAddressRepository, IMapper mapper,
            IConfiguration Configuration, IHttpHelper httpHelper, IJTokenMappingEntityService jTokenMap)
        {
            _next = next;
            _logger = logger;
            ipAddressRepository = IpAddressRepository;
            this.mapper = mapper;
            badIps = new List<IpAddressDto>();
            foreach (var item in (ipAddressRepository.GetIpAddressDto(ip => ip.IsBad == true && ip.IsDelete == false).ToListAsync().Result))
            {
                badIps.Add(item);
            }
            qIpaddress = _qpAddress;
            safeConfiguration = Configuration.GetSection("SafeEntity").Get<SafeEntity>();
            Work();
           _httpHelper = httpHelper;
           _jTokenMap = jTokenMap;
           _pInfoService = new IpInfoService(_jTokenMap, _httpHelper);
        }

        #endregion





        public async Task Invoke(HttpContext context)
        {

            #region 请求
            bool IsBad = false;
            string remoteIp = context.Connection.RemoteIpAddress.MapToIPv4().ToString();

            if (safeConfiguration.IgnoreIps.Where(t => t.Equals(remoteIp.IpToInt()) || remoteIp.Contains("::ffff:127.0.0.1")).Any())
            {
                remoteIp = string.IsNullOrEmpty(context.Request.Headers[safeConfiguration.Key_Header]) ? "" : context.Request.Headers[safeConfiguration.Key_Header];
            }
            long iIp = remoteIp.IpToInt();

            _logger.LogInformation($"\t\tTime:{DateTime.Now}Url:{context.Request.Path};\n\t\tRequest from Remote IP address: {remoteIp},int:{iIp};");


            IsBad = badIps.Where(c => c.IsBad == true && c.IpAddress == iIp).Any();
            if (IsBad)
            {
                _logger.LogInformation($"\t\tForbidden Request from Remote IP address:{remoteIp}");
                context.Response.ContentType = "application/json";
                string result = JsonConvert.SerializeObject(new BaseMessage("你的ip已经被加入黑名单,请联系管理员!", 403, false));
                byte[] bytes = Encoding.UTF8.GetBytes(result);
                await context.Response.Body.WriteAsync(bytes, 0, bytes.Length);
                return;
            }
            else
            {
                AddQueueIp(iIp);
            }
            #endregion

            #region 中间处理
            await _next(context);
            #endregion

            #region 响应

            #endregion

        }




        void AddQueueIp(long ip)
        {
            Lock.EnterWriteLock();
            if (qIpaddress.Where(i => i.IpAddress == ip).Any())
            {
                var IpAddress = qIpaddress.Where(i => i.IpAddress == ip).FirstOrDefault();
                IpAddress.VisitCount++;
                IpAddress.VisitTime = DateTime.Now;
            }
            else
            {
                qIpaddress.Enqueue(new IpAddressDto()
                {
                    StartTime = DateTime.Now,
                    VisitTime = DateTime.Now,
                    Period = safeConfiguration.Period,
                    IpAddress = ip,
                    IsBad = false,
                    VisitCount = 1
                });
            }
            Lock.ExitWriteLock();
        }

        void Work()
        {

            Task.Run(async () =>
            {
                while (true)
                {
                    //Console.WriteLine($"当前线程{Thread.CurrentThread.ManagedThreadId}");
                    if (qIpaddress != null && qIpaddress.Any())
                    {
                        var Ip = qIpaddress.Peek();
                        bool IsOk = false;
                        
                        if (Ip.VisitTime.AddMinutes(Ip.Period) <= DateTime.Now)
                        {
                            Ip.VisitCountPreMinute = Ip.VisitCount.Divide(Ip.StartTime.GetMinuteDiff(Ip.VisitTime)<1?1: Ip.StartTime.GetMinuteDiff(Ip.VisitTime));
                            Ip.IsBad = Ip.VisitCountPreMinute >= safeConfiguration.StandVisitCountPreMinute;
                            try
                            {
                                if (ipAddressRepository.AnyAsync(i => i.IpAddress == Ip.IpAddress).Result)
                                {
                                    IpAddressEntity ipAddressEntity = ipAddressRepository.FirstOrDefaultAsync(i => i.IpAddress == Ip.IpAddress).Result;
                                    if (ipAddressEntity.IsBad)
                                    {
                                        Ip.IsBad = true;
                                    }

                                    mapper.Map(Ip, ipAddressEntity);
                                    if (string.IsNullOrEmpty(ipAddressEntity.Description))
                                    {
                                        IpDto infodto = await _pInfoService.GetIpAddressDtoAsync(Ip.IpAddress.IntToIp());
                                        ipAddressEntity.Description = infodto == null ? "" : infodto.ASN;
                                    }
                                    var data = ipAddressRepository.Update(ipAddressEntity, 0).Result;
                                    IsOk = ipAddressRepository.SaveChangesAsync().Result;
                                    data.State = EntityState.Detached;
                                }
                                else
                                {
                                    IpAddressEntity ipAddressEntity = mapper.Map<IpAddressEntity>(Ip);
                                    IpDto infodto = await _pInfoService.GetIpAddressDtoAsync(Ip.IpAddress.IntToIp());
                                    ipAddressEntity.Description = infodto == null ? "" : infodto.ASN;
                                    var data = ipAddressRepository.Create(ipAddressEntity, 0).Result;
                                    IsOk = ipAddressRepository.SaveChangesAsync().Result;
                                    data.State = EntityState.Detached;
                                }
                                if (Ip.IsBad)
                                {
                                    if (badIps.Where(ip => ip.IpAddress.Equals(Ip.IpAddress)).Any() == false)
                                    {
                                        badIps.Add(new IpAddressDto() { IpAddress = Ip.IpAddress, IsBad = Ip.IsBad });
                                    }
                                }
                                Lock.EnterWriteLock();
                                qIpaddress.Dequeue();
                                Lock.ExitWriteLock();
                            }
                            catch (Exception ex)
                            {

                            }

                        }
                    }
                    else
                    {
                        await Task.Delay(1000 * 60 * 1); //延迟五分钟
                    }

                    await Task.Delay(1000 * 10); //每分钟后检测一次
                }
            });

        }
    }
}
